热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

刷题笔记:探索乘积小于K的子数组问题

篇首语:本文由编程笔记#小编为大家整理,主要介绍了刷题日记乘积小于K的子数组相关的知识,希望对你有一定的参考价值。 乘积小于K的子数组 给定一个正整数数组 nums 和整数 k ,请找出

篇首语:本文由编程笔记#小编为大家整理,主要介绍了刷题日记乘积小于K的子数组相关的知识,希望对你有一定的参考价值。



乘积小于K的子数组

给定一个正整数数组 nums 和整数 k ,请找出该数组内乘积小于 k 的连续子数组个数

示例 1:

输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。

示例 2:

输入: nums = [1,2,3], k = 0
输出: 0

提示:


  • 1 <&#61; nums.length <&#61; 3 * 10^4
  • 1 <&#61; nums[i] <&#61; 1000
  • 0 <&#61; k <&#61; 10^6

题目链接


思路一&#xff1a;前缀和 &#43; 二分

二分查找常用函数&#xff1a;

头文件 #include


  • upper_bound(begin, end, num) &#xff1a;返回在 [begin, end) 二分查找的**第一个大于 num** 的数的索引&#xff0c;不存在返回 end
  • lower_bound(begin, end, num) &#xff1a;返回在 [begin, end) 二分查找的**第一个大于等于 num** 的数的索引&#xff0c;不存在返回 end

算法思路&#xff1a;


  • 使用乘积作为二分的依据&#xff0c;会出现 整形溢出&#xff0c;防止溢出&#xff0c;等式两边取对数&#xff1a;


    • mul[i&#43;1] &#61; mul[i] * nums[i]; //乘积会整型溢出
      // 等式两边取对数
      log(mul[i&#43;1]) &#61; log(mul[i] * nums[i]) &#61; log(mul[i]) &#43; log(nums[i]);
      // 乘积变为对数和
      // 令 sum[i] &#61; log(mul[i])
      sum[i&#43;1] &#61; sum[i] &#43; log(nums[i]);

  • 题目所求为 乘积小于k的子数组&#xff0c;遍历每个数字 nums[i]&#xff0c;枚举区间左端点 i &#xff0c;找最小边界 bound&#xff0c;满足&#xff1a;


    • i <&#61; bound
    • nums[i]*num[i&#43;1]*...*nums[bound] >&#61; ksum[bound] - sum[i-1] >&#61; log(k)
    • 找 第一个bound&#xff0c;sum[bound] >&#61; log(k) &#43; sum[i-1] &#61;&#61;> 使用 lower_bound
  • 注意&#xff1a;因为涉及到对数&#xff0c;因此 sum[] 数组使用 double 类型



    double 类型只保证15位小数精确&#xff0c;为防止计算误差&#xff1a;


    1. 题目中 1 <&#61; nums[i] <&#61; 10001 <&#61; nums.length <&#61; 3 * 10^4&#xff0c;可得最大乘积为1000^3*10^4&#xff0c;取对数为 30000*ln(1000)&#xff0c;ln(1000)&#61;6.9 &#xff0c;整数数位最大为6位

    2. 防止计算误差&#xff08;即15位后省去的小数&#xff09;&#xff0c;小数最少为9位&#xff0c;加上 1e-10

      sum[bound] - sum[i-1] &#43; 1e-10 >&#61; log(k)


    条件&#xff1a;sum[bound] >&#61; log(k) &#43; sum[i -1] - 1e-10

  • 对每个区间左端点 i &#xff0c;找到的边界 bound&#xff0c;则以下区间均满足条件&#xff1a;



    [i, i], [i, i&#43;1], [i, i&#43;2],..., [i, bound-1]


    bound - 1 - i &#43; 1 &#61; bound - i 个区间


代码&#xff1a;

class Solution
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k)
int n &#61; nums.size();
// long long mul[n&#43;1];
double sum[n&#43;1];
for(int i &#61; 0;i < n;i &#43;&#43; )
// mul[i&#43;1] &#61; mul[i] * nums[i]; //乘积会整型溢出
// 等式两边取对数 log(mul[i&#43;1]) &#61; log(mul[i] * nums[i]) &#61; log(mul[i]) &#43; log(nums[i])
// 令 sum[i] &#61; log(mul[i])
sum[i&#43;1] &#61; sum[i] &#43; log(nums[i]);

int cnt &#61; 0;
for(int i &#61; 1;i <&#61; n;i &#43;&#43; )
int l &#61; lower_bound(sum &#43; i, sum &#43; n &#43; 1, sum[i-1] &#43; log(k) - 1e-10) - sum;
cnt &#43;&#61; (l - i);

return cnt;

;
// 遍历每个i 二分找第一个乘积大于等于k的边界bound 则i...bound-1都满足条件

时间复杂度&#xff1a;O(nlogn)

空间复杂度&#xff1a;O(n)


思路二&#xff1a;滑动窗口

双指针 left, right


算法思路&#xff1a;


  • 窗口右边界逐个递增&#xff08;迭代&#xff09;
  • [left, right] 内乘积 prod >&#61; k 时&#xff0c;窗口左边界右移&#xff0c;减小窗口&#xff0c;直到 prod 为止
  • 过程中不断叠加统计 [left, right] 内的新增的子区间数量

【统计子区间】

例&#xff1a;10 5 2 6 k &#61; 101
10 5 2 6
区间1&#xff1a;l,r [10] &#43;1
区间2&#xff1a;l r [10][5][10,5] &#43;1&#43;2
区间3&#xff1a;l r [10][5][10,5][2][5,2][10,5,2] &#43;1&#43;2&#43;3
区间4&#xff1a;l r 10*5*2*6&#61;600 > 101
区间5&#xff1a; l r [6][2,6][5,2,6] &#43;1&#43;2&#43;3&#43;3
l r 结束

注&#xff1a;
1.对每个区间[l,r]只统计包括nums[r]的子区间 &#61;&#61;&#61;> 防止重复
2.对每个满足乘积prod < k的区间统计子区间个数
3.[规律]每个区间增加的子区间个数为 <窗口长度>

代码&#xff1a;

class Solution
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k)
if(k &#61;&#61; 0) return 0;

int n &#61; nums.size();
int l &#61; 0, r &#61; 0;
int prod &#61; 1;
int res &#61; 0;

while(r < n)
prod *&#61; nums[r];
while(l <&#61; r && prod >&#61; k)
prod /&#61; nums[l];
l &#43;&#43; ;

// 满足 prod
if(l <&#61; r)
res &#43;&#61; (r - l &#43; 1);
// 加窗口长度

r &#43;&#43; ;

return res;

;

时间复杂度&#xff1a;O(n)
空间复杂度&#xff1a;O(1)


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • 深入解析JVM垃圾收集器
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版,详细探讨了JVM中不同类型的垃圾收集器及其工作原理。通过介绍各种垃圾收集器的特性和应用场景,帮助读者更好地理解和优化JVM内存管理。 ... [详细]
  • Søren Kierkegaard famously stated that life can only be understood in retrospect but must be lived moving forward. This perspective delves into the intricate relationship between our lived experiences and our reflections on them. ... [详细]
  • 本文将介绍如何使用 Go 语言编写和运行一个简单的“Hello, World!”程序。内容涵盖开发环境配置、代码结构解析及执行步骤。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 深入理解C++中的KMP算法:高效字符串匹配的利器
    本文详细介绍C++中实现KMP算法的方法,探讨其在字符串匹配问题上的优势。通过对比暴力匹配(BF)算法,展示KMP算法如何利用前缀表优化匹配过程,显著提升效率。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文探讨了 C++ 中普通数组和标准库类型 vector 的初始化方法。普通数组具有固定长度,而 vector 是一种可扩展的容器,允许动态调整大小。文章详细介绍了不同初始化方式及其应用场景,并提供了代码示例以加深理解。 ... [详细]
author-avatar
荣哥918
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有